package com.xxx.aop;

import com.alibaba.druid.pool.DruidDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;


/**
 * desc:跑批任务监控
 *
 * @author wangchengwang
 * @date 2018/10/12
 */
@Aspect
public class TaskMonitorAop {
    private static Logger logger = LoggerFactory.getLogger(TaskMonitorAop.class);

    private DruidDataSource dataSource;

    private static ThreadLocal<Map<String, Integer>> mapThreadLocal = new ThreadLocal<>();

    /**
     * 配置作用范围
     */
    @Pointcut("@annotation(com.xxx.aop.TaskMonitor)&&within(com..*)")
    public void aopMethod() {

    }

    public TaskMonitorAop() {
        super();
        logger.info("start TaskMonitorAop");

    }

    /**
     * 前置通知
     */
    @Before("aopMethod()")
    public void before(JoinPoint joinPoint) {
        Connection conn = null;
        Statement stat = null;
        try {
            conn = dataSource.getConnection();
            stat = conn.createStatement();
            String taskMonitorName = getTaskInfo(joinPoint);
            String ip = InetAddress.getLocalHost().getHostAddress();
            String hostName = InetAddress.getLocalHost().getHostName();
            String sqlTemplate = "insert into task_monitor_record (task_name," +
                    " hostname, ip, is_success, start_time," +
                    "   gmt_create) values (?, ?, ?, ?, ?, ?); ";
            PreparedStatement preparedStatement = conn.prepareStatement( sqlTemplate);
            preparedStatement.setString(1,taskMonitorName);
            preparedStatement.setString(2,hostName);
            preparedStatement.setString(3,ip);
            preparedStatement.setInt(4,0);
            preparedStatement.setTimestamp(5,new Timestamp(System.currentTimeMillis()));
            preparedStatement.setLong(6,System.currentTimeMillis());
            preparedStatement.execute();
            stat.executeQuery("select @@identity id;");
            ResultSet rs = stat.getResultSet();
            Integer id = null;
            while (rs.next()) {
                id = rs.getInt(1);
            }
            rs.close();
            Map<String, Integer> taskThreadLocal = new HashMap<>(16);
            taskThreadLocal.put(taskMonitorName, id);
            mapThreadLocal.set(taskThreadLocal);
        } catch (Exception e) {
            logger.error("TaskMonitorAop.before exception", e);

        } finally {
            //关闭数据库资源
            release(stat, conn);
        }
    }


    /**
     * 异常通知
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "aopMethod()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        Connection conn = null;
        Statement stat = null;
        try {
            String taskMonitorName = getTaskInfo(joinPoint);
            String ip = InetAddress.getLocalHost().getHostAddress();

            logger.info("TaskMonitorAop start taskMonitorName={},ip={}", taskMonitorName, ip);
            Integer id = mapThreadLocal.get().remove(taskMonitorName);
            mapThreadLocal.remove();
            if (id != null) {
                conn = dataSource.getConnection();
                stat = conn.createStatement();
                String sqlTemplate = "update task_monitor_record set exception_time=?,is_success=?,gmt_modified=?," +
                        "failure_cause=? where id=?";
                PreparedStatement preparedStatement = conn.prepareStatement( sqlTemplate);
                preparedStatement.setTimestamp(1,new Timestamp(System.currentTimeMillis()));
                preparedStatement.setInt(2,0);
                preparedStatement.setLong(3,System.currentTimeMillis());
                preparedStatement.setString(4,e.toString());
                preparedStatement.setInt(5,id);
                preparedStatement.execute();
            } else {
                logger.error("TaskMonitorAop.doAfterThrowing ip={}, id=null,taskName={}", ip, taskMonitorName);

            }
        } catch (Exception ex) {
            logger.error("TaskMonitorAop.doAfterThrowing exception", ex);
        } finally {
            //关闭数据库资源
            release(stat, conn);
        }

    }


    /**
     * 后置成功通知
     *
     * @param joinPoint
     */
    @AfterReturning(pointcut = "aopMethod()", returning = "retValue")
    public void after(JoinPoint joinPoint, Object retValue) {
        if (retValue == null || !(retValue instanceof TaskMonitorAopResponse)) {
            return;
        }
        Connection conn = null;
        Statement stat = null;
        try {
            TaskMonitorAopResponse taskMonitorAopResponse = (TaskMonitorAopResponse) retValue;
            String taskMonitorName = getTaskInfo(joinPoint);

            String ip = InetAddress.getLocalHost().getHostAddress();

            if (taskMonitorAopResponse.getSuccess() == null) {
                logger.warn("TaskMonitorAop.after ip={} taskMonitorAopResponse.getSuccess()=null,taskName={}", ip,
                        taskMonitorName);
                return;
            }
            logger.info("TaskMonitorAop start taskMonitorName={},ip={}", taskMonitorName, ip);
            Integer id = mapThreadLocal.get().remove(taskMonitorName);
            mapThreadLocal.remove();
            if (id != null) {
                conn = dataSource.getConnection();
                stat = conn.createStatement();
                Timestamp date = new Timestamp(System.currentTimeMillis());

                if (taskMonitorAopResponse.getSuccess()) {
                    String sqlTemplate = "update task_monitor_record set complete_time=?,is_success=?," +
                            "gmt_modified=?,remarks=?,partner_id=?  where id=?";
                    PreparedStatement preparedStatement = conn.prepareStatement( sqlTemplate);
                    preparedStatement.setTimestamp(1,date);
                    preparedStatement.setInt(2,1);
                    preparedStatement.setLong(3,System.currentTimeMillis());
                    preparedStatement.setString(4,taskMonitorAopResponse.getTaskDesc());
                    preparedStatement.setString(5,taskMonitorAopResponse.getPartnerId());
                    preparedStatement.setInt(6,id);
                    preparedStatement.execute();
                } else {
                    String sqlTemplate = "update task_monitor_record set exception_time=?,is_success=?," +
                            "gmt_modified=?, failure_cause=? , partner_id=? where id=?";
                    PreparedStatement preparedStatement = conn.prepareStatement( sqlTemplate);
                    preparedStatement.setTimestamp(1,date);
                    preparedStatement.setInt(2,0);
                    preparedStatement.setLong(3,System.currentTimeMillis());
                    preparedStatement.setString(4,taskMonitorAopResponse.getFailureCause());
                    preparedStatement.setString(5,taskMonitorAopResponse.getPartnerId());
                    preparedStatement.setInt(6,id);
                    preparedStatement.executeUpdate();

                }

            } else {
                logger.error("TaskMonitorAop.after ip={},id=null,taskName={}", ip, taskMonitorName);

            }

        } catch (Exception ex) {
            logger.error("TaskMonitorAop.after exception", ex);

        } finally {
            // 关闭数据库资源.
            release(stat, conn);
        }
    }

    private String getTaskInfo(JoinPoint joinPoint) throws Exception{
        String taskMonitorName;
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        TaskMonitor taskMonitor = method.getAnnotation(TaskMonitor.class);
        if(taskMonitor!=null&&StringUtils.isNotEmpty(taskMonitor.taskName())) {
            taskMonitorName = taskMonitor.taskName();
        }else{
            String[] classPath = joinPoint.getSignature().getDeclaringTypeName().split("[^a-z]");
            String project = classPath[3];
            String simpleClassName = joinPoint.getSignature().getDeclaringType().getSimpleName();
            String methodName = joinPoint.getSignature().getName();
            taskMonitorName = project + "." + simpleClassName + "." + methodName;
        }
        return taskMonitorName;
    }

    /**
     * 关闭 Statement 和 Connection
     * @param statement
     * @param conn
     */
    public static void release(Statement statement, Connection conn) {
        if (statement != null) {
            try {
                statement.close();
            } catch (Exception e) {
                logger.error("TaskMonitorAop.release exception",e);
            }

        }

        if (conn != null) {
            try {
                conn.close();
            } catch (Exception e) {
                logger.error("TaskMonitorAop.release exception",e);

            }
        }
    }

    public void setDataSource(DruidDataSource dataSource) {
        this.dataSource = dataSource;
    }
}